#include <stdio.h>
#include <string.h>
#include <unistd.h>

#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include <liburing.h>

#define ENTRIES_LENGTH      1024

enum {
    EVENT_ACCEPT = 0,
    EVENT_READ,
    EVENT_WRITE   
};

typedef struct _conninfo{
    int connfd;
    int event;
}conninfo;

//设置accept事件所需的信息，并将其与相应的 SQE 关联起来
void set_accept_event(struct io_uring *ring, int sockfd, struct sockaddr *addr, 
                        socklen_t *addrlen, int flags){
    //获取一个SQE
    struct io_uring_sqe *sqe = io_uring_get_sqe(ring);

    //准备执行accept操作的SQE
    io_uring_prep_accept(sqe, sockfd, addr, addrlen, flags);

    //创建了一个结构体变量 info_accept，其中存储了与连接相关的信息
    conninfo info_accept = {
        .connfd = sockfd,
        .event = EVENT_ACCEPT
    };

    memcpy(&sqe->user_data, &info_accept, sizeof(info_accept));

}

//设置recv事件所需的信息，并将其与相应的 SQE 关联起来
void set_recv_event(struct io_uring *ring, int sockfd, void *buf, size_t len, int flags){

    struct io_uring_sqe *sqe = io_uring_get_sqe(ring);

    io_uring_prep_recv(sqe, sockfd, buf, len, flags);

    conninfo info_recv = {
        .connfd = sockfd,
        .event = EVENT_READ
    };

    memcpy(&sqe->user_data, &info_recv, sizeof(info_recv));
}

//设置send事件所需的信息，并将其与相应的 SQE 关联起来
void set_send_event(struct io_uring *ring, int sockfd, void *buf, size_t len, int flags){

    struct io_uring_sqe *sqe = io_uring_get_sqe(ring);

    io_uring_prep_send(sqe, sockfd, buf, len, flags);

    conninfo info_send = {
        .connfd = sockfd,
        .event = EVENT_WRITE
    };

    memcpy(&sqe->user_data, &info_send, sizeof(info_send));
}


int main(){

    int sockfd = socket(AF_INET, SOCK_STREAM, 0);

    struct sockaddr_in servaddr;
    memset(&servaddr, 0, sizeof(struct sockaddr_in));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(9999);

    if (-1 == bind(sockfd, (struct sockaddr *)&servaddr, sizeof(struct sockaddr))){
        printf("bind failed:%s\n",strerror(errno));
        return -1;
    }

    listen(sockfd, 10);

    //linuring


    struct io_uring_params params;
    memset(&params, 0 ,sizeof(struct io_uring_params));

    struct io_uring ring;
    //初始化io_uring队列
    io_uring_queue_init_params(ENTRIES_LENGTH, &ring, &params);

    //获取一个SQE
    struct io_uring_sqe *sqe = io_uring_get_sqe(&ring);

    struct sockaddr_in clientaddr;
    socklen_t clilen = sizeof(struct sockaddr);

    //设置accept事件所需的信息，并将其与相应的 SQE 关联起来
    set_accept_event(&ring, sockfd, (struct sockaddr *)&clientaddr, &clilen, 0);

    char buffer[1024] = {0};

    while (1){

        //提交 SQE
        io_uring_submit(&ring);

        struct io_uring_cqe *cqe;
        //等待完成队列项cqe
        io_uring_wait_cqe(&ring, &cqe);

        struct io_uring_cqe *cqes[10];
        //批量获取已完成IO操作的CQE
        int cqecount = io_uring_peek_batch_cqe(&ring, cqes, 10);

        int i = 0;
        for (i = 0; i < cqecount; i++){

            cqe = cqes[i];

            conninfo ci;
            memcpy(&ci, &cqe->user_data, sizeof(ci));

            if (ci.event == EVENT_ACCEPT){ //此时已经完成accept
                if (cqe->res < 0) continue;
                int connfd = cqe->res; 
                //在已接受连接的connfd上设置recv事件处理器，以便处理已建立连接的数据接收操作。
                set_recv_event(&ring, connfd, buffer, 1024, 0); //此处的connfd表示已建立连接的fd，clientfd
                //重新设置接受连接的事件，以便处理新连接的接受操作。
                set_accept_event(&ring, ci.connfd, (struct sockaddr *)&clientaddr, &clilen, 0); //此处的connfd表示新连接的fd，listenfd
            }else if (ci.event == EVENT_READ){//此时已经完成read
                if (cqe->res < 0) continue;
                if (cqe->res == 0){
                    close(ci.connfd);
                }else{
                    printf("recv --> %s, %d\n", buffer, cqe->res);
                    //重新设置写的事件，以便回发
                    set_send_event(&ring, ci.connfd, buffer, cqe->res, 0);
                }
            } else if (ci.event == EVENT_WRITE){//此时已经完成write
                //重新设置读的事件
                set_recv_event(&ring, ci.connfd, buffer, 1024, 0);
            }
        }
        io_uring_cq_advance(&ring, cqecount);
    }

    getchar();

}